package org.xx.armory.service.data.mysql;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xx.armory.commons.DateTimeUtils;
import org.xx.armory.db.DbRow;
import org.xx.armory.db.Mapper;
import org.xx.armory.db.Session;
import org.xx.armory.rpc.Transactional;
import org.xx.armory.service.AbstractService;
import org.xx.armory.service.ServiceException;
import org.xx.armory.service.data.DictItem;
import org.xx.armory.service.data.DictProvider;

import java.util.Collection;
import java.util.Date;
import java.util.Optional;

import static org.xx.armory.commons.Validators.notNull;

/**
 * 使用MySql数据库实现的字典提供程序。
 *
 * @author Haart
 */
public class MySqlDictProvider
        extends AbstractService
        implements DictProvider {
    /**
     * 键字段的最大长度（以字符为单位）。
     */
    private static final int MAX_KEY_SIZE = 200;
    /**
     * 值字段的最大长度（以字符为单位）。
     */
    private static final int MAX_VALUE_SIZE = 2000;
    @SuppressWarnings("unused")
    private Logger logger = LoggerFactory.getLogger(MySqlDictProvider.class);

    /**
     *
     */
    public MySqlDictProvider() {
    }

    private static DictItem toDictItem(
            DbRow row
    ) {
        notNull(row, "row");

        return new DictItem(row.getString(0), row.getString(1), row.getString(2));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(readOnly = false)
    public final void put(
            int categoryId,
            DictItem item
    ) {
        try (final Session session = getSessionFactory().newSession()) {
            session.newParameter("category_id").setValue(categoryId).then()
                   .newParameter("key").setSize(MAX_KEY_SIZE).setValue(item.getKey().trim()).then()
                   .newParameter("value").setSize(MAX_VALUE_SIZE).setValue(item.getValue()).then()
                   .newParameter("parent_key").setSize(MAX_KEY_SIZE).setValue(item.getParentKey()).then()
                   .newParameter("last_write_time").setValue(new Date()).then()
                   .update("INSERT INTO MY_DICT (`CATEGORY_ID`, `KEY`, `VALUE`,\n"
                                   + "`ENABLED`, PARENT_KEY, LAST_WRITE_TIME)\n"
                                   + "VALUES (#category_id#, #key#, #value#, 1, #parent_key#, #last_write_time#)");
        } catch (Exception ex) {
            throw new ServiceException(ex);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional
    public final Optional<DictItem> get(
            int categoryId,
            String key
    ) {
        try (final Session session = getSessionFactory().newSession()) {
            return session.newParameter("category_id").setValue(categoryId).then()
                          .newParameter("key").setSize(MAX_KEY_SIZE).setValue(key == null ? "" : key.toLowerCase()).then()
                          .querySingle("SELECT  `KEY`, `VALUE`, `PARENT_KEY` FROM MY_DICT\n"
                                               + "WHERE CATEGORY_ID = #category_id#\n"
                                               + "  AND `ENABLED` = 1\n"
                                               + "AND `KEY` = #key#", MySqlDictProvider::toDictItem);
        } catch (Exception ex) {
            throw new ServiceException(ex);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional
    public final Collection<DictItem> getAll(int categoryId) {
        try (final Session session = getSessionFactory().newSession()) {
            return session.newParameter("category_id").setValue(categoryId).then()
                          .query("SELECT  `KEY`, `VALUE`, `PARENT_KEY` FROM MY_DICT\n"
                                         + "WHERE CATEGORY_ID = #category_id#\n"
                                         + "  AND `ENABLED` = 1\n"
                                         + "ORDER BY `KEY`", MySqlDictProvider::toDictItem);
        } catch (Exception ex) {
            throw new ServiceException(ex);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(readOnly = false)
    public final void delete(
            int categoryId,
            String key
    ) {
        try (final Session session = getSessionFactory().newSession()) {
            session.newParameter("category_id").setValue(categoryId).then()
                   .newParameter("key").setSize(MAX_KEY_SIZE).setValue(key == null ? "" : key.toLowerCase().trim()).then()
                   .update("UPDATE MY_DICT SET `ENABLED` = 0\n"
                                   + "WHERE CATEGORY_ID = #category_id#\n"
                                   + "AND `KEY` = #key#");
        } catch (Exception ex) {
            throw new ServiceException(ex);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional
    public final Date getLastWriteTime(
            int categoryId
    ) {
        try (final Session session = getSessionFactory().newSession()) {
            return session.newParameter("category_id").setValue(categoryId).then()
                          .querySingle("SELECT MAX(LAST_WRITE_TIME) FROM MY_DICT\n"
                                               + "WHERE CATEGORY_ID = #category_id#\n", Mapper::toDate)
                          .orElse(DateTimeUtils.minValue());
        } catch (Exception ex) {
            throw new ServiceException(ex);
        }
    }
}
